package suery.syntax;

import java.awt.Component;
import java.lang.reflect.*;
import java.util.Queue;
import java.util.Set;

import suery.ComponentTreeNode;
import suery.SueryException;

/**
 * '.' in expression means to get the field of the component.</br> First this
 * parser try to access the field by search the getter method. </br>If the
 * method cannot be found, then the parser will try to access the field of the
 * component directly.
 * 
 * @author Jeky
 * 
 */
public class GetFieldParser implements TokenParser {

    @Override
    public void parse(Queue<String> tokenQueue,
            Set<ComponentTreeNode> parents,
            Set<ComponentTreeNode> results) {
        String token = tokenQueue.peek();
        Component component = null;
        for (ComponentTreeNode p : parents) {
            component = getByMethod(token, p);
            if (component != null) {
                break;
            }
            component = getByField(token, p);
            if (component != null) {
                break;
            }
        }
        if (component != null) {
            new SearchByRefParser(component)
                    .parse(tokenQueue, parents, results);
        }
    }

    private Component getByField(String token, ComponentTreeNode parent) {
        Component component = parent.getComponent();
        if (component == null) {
            return null;
        }
        Class<?> componentClass = component.getClass();
        Field[] fields = componentClass.getDeclaredFields();
        for (Field f : fields) {
            if (f.getName().equals(token)) {
                try {
                    f.setAccessible(true);
                    return (Component) f.get(component);
                } catch (Exception e) {
                    throw new SueryException.Parse(token, e);
                }
            }

        }
        return null;
    }

    private Component getByMethod(String token, ComponentTreeNode parent) {
        Component component = parent.getComponent();
        if (component == null) {
            return null;
        }
        Class<?> componentClass = component.getClass();
        Method[] methods = componentClass.getDeclaredMethods();
        for (Method m : methods) {
            if (m.getParameterTypes().length == 0) {
                if (m.getName().equalsIgnoreCase("get" + token)) {
                    try {
                        return (Component) m.invoke(component, new Object[0]);
                    } catch (Exception e) {
                        throw new SueryException.Parse(token, e);
                    }
                }
            }
        }
        return null;
    }
}
